package com.swak.persistence.mybatis.filter;

import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Invocation;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import com.swak.annotation.MS;
import com.swak.persistence.datasource.DataSourceHolder;
import com.swak.utils.StringUtils;

/**
 * 主从插件
 * 
 * 如果已经使用 @Transactional 开启了事务，则在此 Filter
 * 中不会设置有效；
 * 
 * 如果不使用 @Transactional 则会多条数据库操作获取多次连接，即
 * 每条 sql 都会有自己的事务， 效率不高；
 * 
 * 可以配合 @DynamicDataSourceAspect 来主动设置数据源
 * 
 * @author lifeng
 * @date 2021年2月2日 上午10:07:29
 */
public class MsingFilter implements Filter {

	@Override
	public Object filter(Invocation invocation, FilterChain chain) throws Throwable {

		/*
		 * 开启了多数据源的配置
		 */
		if (!DataSourceHolder.isDynamicDataSource()) {
			return chain.filter(invocation);
		}

		/*
		 * 如果已经开启了事务，则不需要处理了
		 */
		boolean syschronizationActive = TransactionSynchronizationManager.isActualTransactionActive();
		if (syschronizationActive) {
			return chain.filter(invocation);
		}

		// 默认走主库
		String lookupKey = MS.Master.name();

		// 参数
		Object[] objects = invocation.getArgs();
		MappedStatement mappedStatement = (MappedStatement) objects[0];

		// 查询 -- 但是要排除： for update, 排除：SELECT LAST_INSERT_ID()；
		if (mappedStatement.getSqlCommandType() == SqlCommandType.SELECT) {

			// 同时不是 for update -- 所有的 for update 需要以lock结尾
			String sqlId = mappedStatement.getId();
			if (!StringUtils.endsWithIgnoreCase(sqlId, com.swak.define.CurdDefine.Lock_Sql_Statement)) {
				lookupKey = MS.Slave.name();
			}
		}

		// 设置数据源
		DataSourceHolder.setDataSourceType(lookupKey);
		try {
			return chain.filter(invocation);
		} finally {
			DataSourceHolder.clearDataSourceType();
		}
	}
}
